from pos import Position
import stackless
from event import Event
import sleep

from collections import defaultdict as dict_

from actor import Actor

from log import getLogger

logger=getLogger('world')

def testCollision(self,other):

    logger.debug(repr(self)+repr(other))

    bx1,by1,bx2,by2=other
    ax1,ay1,ax2,ay2=self

    for x,y in [(ax1,ay1),(ax1,ay2),(ax2,ay1),(ax2,ay2)]:
        if x >= bx1 and x <= bx2 and y >= by1 and y <= by2:
            return True

    for x,y in [(bx1,by1),(bx1,by2),(bx2,by1),(bx2,by2)]:
        if x >= ax1 and x <= ax2 and y >= ay1 and y <= ay2:
            return True

    return False

def pos2rect(pos,unitsize):
    return (pos[0]-unitsize[0]/2,pos[0]+unitsize[0]/2,pos[1]-unitsize[1]/2,pos[1]+unitsize[1]/2)

class World(Actor):

    def __init__(self,display):

        Actor.__init__(self)

        self.display=display
        display.world=self

        self.quit=False

        self.sleepManager=sleep.SleepManager()
        self.pos=(self.display.size[0] / 2,self.display.size[1] / 2)

        self.clear=False

        self.map_=dict_(lambda *args,**kwargs : set())

    def defaultEventHandler(self):

        dirtyPos=[]
        self.actors={}
        self.charectors=set()
        self.gold=0

        while True :
            event=self.channel.receive()

            if event.subject=='join':

                dirtyPos.append(event.pos)

                if hasattr(event.obj,'channel') : # if not It is not an actor at all
                    self.actors[event.obj]=event.pos

                self.map_[event.pos].add(event.obj)

                if hasattr(event,'IsCharector') and event.IsCharector :

                    logger.debug('joined charector')
                    self.charectors.add(event.obj)

                if hasattr(event.obj,'IsGold') and event.obj.IsGold:
                    self.gold+=1

            elif event.subject=='lose':

                self.display.lose()

            elif event.subject=='move':

                old_pos=self.actors[event.obj]
                #dirtyPos.append(old_pos)
                dirtyPos.append(event.pos)

                self.map_[old_pos].discard(event.obj)
                if not self.map_[old_pos]:
                    del self.map_[old_pos]

                self.map_[event.pos].add(event.obj)

                self.actors[event.obj]=event.pos

            elif event.subject=='remove':

                pos=event.pos
                if isinstance(event.obj,Actor):
                    del self.actors[event.obj]

                if hasattr(event.obj,'IsGold') and event.obj.IsGold:
                    self.gold-=1

                self.charectors.discard(event.obj)

                self.map_[pos].discard(event.obj)

                if not self.map_[pos]:
                    del self.map_[pos]

                dirtyPos.append(pos)

            elif event.subject=='stage_clear':
                self.clear=True

                stackless.tasklet(self.display.stage_clear)()

            elif event.subject=='input':
                if self.clear :
                    stackless.tasklet(self.channel.send)(Event(subject='quit'))
                self.sendInput(event.queue)

            elif event.subject=='quit':

                self.broadcast(event)

                self.quit=True
                break

            #Ticking self

            if dirtyPos :
                stackless.tasklet(self.display.update)(positions=dirtyPos)

                dirtyPos=[]

            stackless.schedule()

    def getCollisions(self,pos,obj=None):

        display=self.display
        size=display.unitsize
        rect=pos.toRect(size)

        if (pos.x<0) or (pos.y<0) or (pos.x>display.size[0]) or (pos.y>display.size[1]):

            logger.debug('size : %s,%s' % self.display.size)
            logger.debug('maybe it is out of range')
            res=[self]
        else :
            res=[]
        
        if pos in self.map_:
            collisions=self.map_[pos].copy()
            collisions.discard(obj)

            return res+list(collisions)
        else :
            return res

    def broadcast(self,event):
        for obj in self.actors.iterkeys() :
            obj.channel.send(event)

        self.sleepManager.channel.send(event)

    def sendInput(self,queue):

        logger.debug(queue)
        
        for obj in self.charectors:

            for event in queue :
                obj.channel.send(Event(subject='input',event=event))
